<!DOCTYPE html>
<html>

<head>
    <title>extrudelinetrail test</title>
    <script type="text/javascript" src="https://unpkg.com/dat.gui@0.7.6/build/dat.gui.min.js"></script>
    <link type="text/css" rel="stylesheet" href="https://unpkg.com/maptalks/dist/maptalks.css">
    <script type="text/javascript" src="https://unpkg.com/maptalks/dist/maptalks.js"></script>
    <script type="text/javascript" src="https://unpkg.com/@maptalks/gl/dist/maptalksgl.js"></script>
    <script type="text/javascript" src="https://unpkg.com/three@0.138.0/build/three.min.js"></script>

    <script type="text/javascript" src="https://unpkg.com/three@0.104.0/examples/js/libs/stats.min.js"></script>
    <script type="text/javascript" src="https://unpkg.com/lz-string@1.4.4/libs/lz-string.min.js"></script>
    <script type="text/javascript" src="https://unpkg.com/maptalks.three@latest/dist/maptalks.three.js"></script>
    <script type="text/javascript" src="js/geoutil.js"></script>
    <script type="text/javascript" src="buildings.js"></script>
    <style>
        html,
        body {
            margin: 0px;
            height: 100%;
            width: 100%;
        }

        #map {
            width: 100%;
            height: 100%;
            background-color: #000;
        }
    </style>
</head>

<body>
    <div id="map"></div>
    <script>

        var baseLayer = new maptalks.TileLayer('tile', {
            urlTemplate: 'https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}.png',
            subdomains: ['a', 'b', 'c', 'd'],
            attribution: '&copy; <a href="http://osm.org">OpenStreetMap</a> contributors, &copy; <a href="https://carto.com/">CARTO</a>'
        });

        var map = new maptalks.Map("map", {
            center: [13.412830873144912, 52.53720286413957],
            zoom: 16,
            pitch: 70,
            bearing: 180,
            centerCross: true,
            doubleClickZoom: false
        });
        // features to draw
        var features = [];
        buildings.forEach(function (b) {
            features = features.concat(b.features);
        });
        // the ThreeLayer to draw buildings
        var threeLayer = new maptalks.ThreeLayer('t', {
            forceRenderOnMoving: true,
            forceRenderOnRotating: true,
            // animation: true
        });
        var stats;

        const texture = new THREE.TextureLoader().load('./data/building-texture-dark2.jpg');
        texture.needsUpdate = true; //使用贴图时进行更新
        texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
        // texture.repeat.set(0.002, 0.002);
        texture.repeat.set(1, 1);

        var buildingMaterial = new THREE.MeshBasicMaterial({ color: 'rgb(255,255,255)', map: texture });
        var buildingTopMaterial = new THREE.MeshBasicMaterial({ color: 'rgb(85,83,83)' });
        var buildingMeshes = [], buildingRoof;

        function cloeData(data) {
            if (window.ststructuredClone) {
                return window.ststructuredClone(data);
            }
            return JSON.parse(JSON.stringify(data));
        }

        threeLayer.prepareToDraw = function (gl, scene, camera) {
            stats = new Stats();
            stats.domElement.style.zIndex = 100;
            document.getElementById('map').appendChild(stats.domElement);

            var light = new THREE.DirectionalLight(0xffffff);
            light.position.set(0, -10, 10).normalize();
            scene.add(light);

            const topData = [];

            features.forEach(function (g) {
                var heightPerLevel = 10;
                var levels = g.properties.levels || 1;
                const height = heightPerLevel * levels;
                g.properties.height = height;
                var mesh = threeLayer.toExtrudePolygon(g, {
                    height,
                    topColor: '#fff',
                    asynchronous: true,
                    interactive: false
                }, buildingMaterial);
                buildingMeshes.push(mesh);
                const f = cloeData(g);
                f.properties.height = 0.1;
                f.properties.bottomHeight = height;
                topData.push(f);
            });
            threeLayer.addMesh(buildingMeshes);
            buildingRoof = threeLayer.toExtrudePolygons(topData, { asynchronous: true, interactive: false }, buildingTopMaterial);
            threeLayer.addMesh(buildingRoof);
            addLines();

        };
        const sceneConfig = {
            postProcess: {
                enable: true,
                antialias: { enable: true },
                bloom: {
                    enable: true,
                    threshold: 0,
                    factor: 1,
                    radius: 0.02,
                },
            }
        };
        const groupLayer = new maptalks.GroupGLLayer('group', [threeLayer], { sceneConfig });
        groupLayer.addTo(map);

        var baseLineMaterial = new THREE.LineBasicMaterial({
            linewidth: 1,
            color: 'rgb(255,90,0)',
            // opacity: 0.5,
            blending: THREE.AdditiveBlending,
            transparent: true
        });
        var baseLines = [];

        function addLines() {
            fetch('./data/berlin-roads.txt').then(function (res) {
                return res.text();
            }).then(function (geojson) {
                geojson = LZString.decompressFromBase64(geojson);
                geojson = JSON.parse(geojson);
                var lineStrings = maptalks.GeoJSON.toGeometry(geojson);
                var data = [], data1 = [];
                var classMap = {};
                lineStrings.forEach(lineString => {
                    const fclass = lineString.getProperties().fclass;
                    classMap[fclass] = fclass;
                    //main road
                    if (fclass && (fclass.includes('primary') || fclass.includes('secondary') || fclass.includes('tertiary'))) {
                        data1.push(lineString);
                    } else {
                        data.push(lineString);
                    }
                });
                const list = [];
                data.forEach(lineString => {
                    list.push({
                        lineString,
                        len: lineLength(lineString)
                    });
                });
                data = list.sort(function (a, b) {
                    return b.len - a.len
                });

                baseLines = data.slice(0, 200).map(function (d) {
                    var line = threeLayer.toLine(d.lineString, { bloom: true, interactive: false }, baseLineMaterial);
                    return line;
                });
                threeLayer.addMesh(baseLines);

                addExtrudeLine(data1);

            });
        }

        var material = new THREE.MeshBasicMaterial({
            color: 'rgb(255,45,0)', transparent: true, blending: THREE.AdditiveBlending
        });
        var highlightmaterial = new THREE.MeshBasicMaterial({ color: '#ffffff', transparent: true });
        var lines, lineTrails;

        function addExtrudeLine(lineStrings) {
            var timer = 'generate line time';
            console.time(timer);
            const list = [];
            lineStrings.forEach(lineString => {
                list.push({
                    lineString,
                    len: lineLength(lineString)
                });
            });
            lineStrings = list.sort(function (a, b) {
                return b.len - a.len
            });

            lines = lineStrings.slice(0, 1000).map(d => {
                var line = threeLayer.toExtrudeLine(d.lineString, { altitude: 0, width: 4, height: 1, bloom: true, interactive: false, asynchronous: true }, material);
                return line;
            });
            lineTrails = lineStrings.slice(0, 300).map(function (d) {
                var line = threeLayer.toExtrudeLineTrail(d.lineString, { altitude: 0, width: 5, height: 2, chunkLength: d.len / 40, speed: 1, trail: 6, bloom: true, interactive: false, asynchronous: true }, highlightmaterial);
                return line;
            });

            console.log('lines.length:', lines.length);
            console.timeEnd(timer);
            threeLayer.addMesh(lines);
            threeLayer.addMesh(lineTrails);
            initGui();
            // threeLayer.config('animation', true);
            animation();
        }

        function animation() {
            // layer animation support Skipping frames
            threeLayer._needsUpdate = !threeLayer._needsUpdate;
            if (threeLayer._needsUpdate && !threeLayer.isRendering()) {
                threeLayer.redraw();
            }
            stats.update();
            requestAnimationFrame(animation);
        }


        function initGui() {
            var bloomPass = threeLayer.bloomPass;
            var params = {
                tilelayer: false,

                buildingAdd: true,
                buildingColor: buildingMaterial.color.getStyle(),
                buildingAnimation: function () {
                    buildingMeshes.forEach(function (mesh) {
                        mesh.animateShow({
                            duration: 3000
                        });
                    });
                },
                buildingRoofAdd: true,
                buildingRoofColor: buildingTopMaterial.color.getStyle(),

                lineAdd: true,
                lineColor: baseLineMaterial.color.getStyle(),

                extrudeLineAdd: true,
                extrudeLineColor: material.color.getStyle(),

                extrudeLineTrailAdd: true,
                extrudeLineTrailColor: highlightmaterial.color.getStyle(),

                bloom: true,
                // threshold: bloomPass.threshold,
                // strength: bloomPass.strength,
                // radius: bloomPass.radius,

                color: highlightmaterial.color.getStyle(),
                show: true,
                opacity: 1,
                altitude: 0
            };
            var gui = new dat.GUI({
                width: 300
            });

            var tileLayerF = gui.addFolder('tilelayer');
            tileLayerF.open();
            tileLayerF.add(params, 'tilelayer').name('add').onChange(function () {
                if (params.tilelayer) {
                    map.setBaseLayer(baseLayer);
                } else {
                    map.removeBaseLayer(baseLayer);
                }
            });

            var buildingsF = gui.addFolder('building');
            buildingsF.open();
            buildingsF.add(params, 'buildingAdd').name('add').onChange(function () {
                if (params.buildingAdd) {
                    threeLayer.addMesh(buildingMeshes);
                } else {
                    threeLayer.removeMesh(buildingMeshes);
                }
            });
            buildingsF.addColor(params, 'buildingColor').name('color').onChange(function () {
                buildingMaterial.color.set(params.buildingColor);
                buildingMeshes.forEach(mesh => {
                    mesh.setSymbol(buildingMaterial);
                });
            });

            buildingsF.add(params, 'buildingRoofAdd').name('roof').onChange(function () {
                if (params.buildingRoofAdd) {
                    threeLayer.addMesh(buildingRoof);
                } else {
                    threeLayer.removeMesh(buildingRoof);
                }
            });
            buildingsF.addColor(params, 'buildingRoofColor').name('roof color').onChange(function () {
                buildingTopMaterial.color.set(params.buildingRoofColor);
            });
            // buildingsF.add(params, 'buildingAnimation').name('animation');


            var baseLineF = gui.addFolder('line');
            baseLineF.open();
            baseLineF.add(params, 'lineAdd').name('add').onChange(function () {
                if (params.lineAdd) {
                    threeLayer.addMesh(baseLines);
                } else {
                    threeLayer.removeMesh(baseLines);
                }
            });
            baseLineF.addColor(params, 'lineColor').name('color').onChange(function () {
                baseLineMaterial.color.set(params.lineColor);
                baseLines.forEach(mesh => {
                    mesh.setSymbol(baseLineMaterial);
                });
            });

            var extrudeLineF = gui.addFolder('extrudeLine');
            extrudeLineF.open();
            extrudeLineF.add(params, 'extrudeLineAdd').name('add').onChange(function () {
                if (params.extrudeLineAdd) {
                    threeLayer.addMesh(lines);
                } else {
                    threeLayer.removeMesh(lines);
                }
            });
            extrudeLineF.addColor(params, 'extrudeLineColor').name('color').onChange(function () {
                material.color.set(params.extrudeLineColor);
                lines.forEach(mesh => {
                    mesh.setSymbol(material);
                });
            });


            var extrudeLineTrailF = gui.addFolder('extrudeLineTrail');
            extrudeLineTrailF.open();
            extrudeLineTrailF.add(params, 'extrudeLineTrailAdd').name('add').onChange(function () {
                if (params.extrudeLineTrailAdd) {
                    threeLayer.addMesh(lineTrails);
                } else {
                    threeLayer.removeMesh(lineTrails);
                }
            });
            extrudeLineTrailF.addColor(params, 'extrudeLineTrailColor').name('color').onChange(function () {
                highlightmaterial.color.set(params.extrudeLineTrailColor);
                lineTrails.forEach(mesh => {
                    mesh.setSymbol(highlightmaterial);
                });
            });

            // var bloomF = gui.addFolder('bloom');
            // bloomF.open();
            // bloomF.add(params, 'bloom').name('enable').onChange(function () {
            //     threeLayer.bloomEnable = params.bloom;
            // });
            // bloomF.add(params, 'threshold', -5, 5).onChange(function () {
            //     bloomPass.threshold = params.threshold;
            // });
            // bloomF.add(params, 'strength', 0, 8).onChange(function () {
            //     bloomPass.strength = params.strength;
            // });
            // bloomF.add(params, 'radius', 0, 5).onChange(function () {
            //     bloomPass.radius = params.radius;
            // });
        }
    </script>
</body>

</html>